#pragma once

#include "vulkan_include.hpp"
#include <GLFW/glfw3.h>

#include <string>
#include <vector>

#include "njm_device.hpp"

namespace njm {

    struct PipelineConfigInfo
    {
        PipelineConfigInfo() = default;
        PipelineConfigInfo(const PipelineConfigInfo&) = delete;
        PipelineConfigInfo& operator=(const PipelineConfigInfo&) = delete;
        
        std::vector<VkVertexInputBindingDescription> bindingDescriptions{};
        std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
        // VkViewport viewport;
        // VkRect2D scissor;
        VkPipelineViewportStateCreateInfo viewportInfo;
        VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
        VkPipelineRasterizationStateCreateInfo rasInfo;
        VkPipelineMultisampleStateCreateInfo msInfo;
        VkPipelineColorBlendAttachmentState colorblendAttachment;
        VkPipelineColorBlendStateCreateInfo colorblendInfo;
        VkPipelineDepthStencilStateCreateInfo depthstencilInfo;
        std::vector<VkDynamicState> dynamicStateEnables;
        VkPipelineDynamicStateCreateInfo dynamicStateInfo;
        VkPipelineLayout pipelineLayout = nullptr;
        VkRenderPass renderPass = nullptr;
        uint32_t subpass = 0;
    };
    
    class NjmPipeline
    {
    private:
        NjmDevice& njmDevice;
        VkPipeline graphicsPipeline;
        VkShaderModule vertShaderModule;
        VkShaderModule fragShaderModule;

        static std::vector<char> readFile(const std::string& filePath);

        void createGraphicsPipeline(
            const std::string& vertPath, 
            const std::string& fragPath,
            const PipelineConfigInfo& configInfo);

        void createShaderModule(
            const std::vector<char>& code, 
            VkShaderModule *shaderModule);


    public:
        NjmPipeline(
            NjmDevice& device,
            const std::string& vertPath, 
            const std::string& fragPath,
            const PipelineConfigInfo& configInfo);
        ~NjmPipeline();
        NjmPipeline(const NjmPipeline&) = delete;
        NjmPipeline& operator=(const NjmPipeline&) = delete;

        void bind(VkCommandBuffer commandBuffer);

        static void defaultPipelineConfigInfo(PipelineConfigInfo& pipelineInfo);
        static void enableAlphaBlending(PipelineConfigInfo& pipelineInfo);
        static void disableDepthWrite(PipelineConfigInfo& pipelineInfo);
    };
    
}